package com.hw01.service.impl;

import java.io.*;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.security.AccessController;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.hw01.common.Result;
import com.hw01.common.ResultPage;
import com.hw01.common.UserInfo;
import com.hw01.constant.DelState;
import com.hw01.dto.PayrollComputationDto;
import com.hw01.dto.PayrollsQueryDto;
import com.hw01.entity.Employee;
import com.hw01.entity.Payrolls;
import com.hw01.mapper.PayrollsMapper;
import com.hw01.service.PayrollsService;
import com.hw01.utils.DateUtils;
import com.hw01.vo.PayrollsVo;

import sun.security.action.GetPropertyAction;

/**
 * <p>
 * 工资单 服务实现类
 * </p>
 *
 * @author zhong
 * @since 2024-03-18
 */
@Service
public class PayrollsServiceImpl extends ServiceImpl<PayrollsMapper, Payrolls> implements PayrollsService {

    @Autowired
    private PayrollsMapper mapper;

    @Override
    public Result<List<PayrollsVo>> getPayrollsList(Long userId, String year, String month) {

        if (userId == null) {
            return Result.error("确实参数员工id(userId)");
        }

        MPJLambdaWrapper<Payrolls> wrapper = new MPJLambdaWrapper<Payrolls>().selectAll(Payrolls.class)
            .selectAs(Employee::getUserName, PayrollsVo::getUserName)
            .selectAs(Employee::getBasicSalary, PayrollsVo::getBasicSalary)
            // 员工
            .innerJoin(Employee.class, Employee::getId, Payrolls::getUserId)
            .eq(Employee::getDelState, DelState.UNDELETED).eq(Employee::getId, userId)
            // 工资单
            .eq(StringUtils.isNotBlank(year), Payrolls::getYear, year)
            .eq(StringUtils.isNotBlank(month), Payrolls::getMonth, month);

        List<PayrollsVo> list = mapper.selectJoinList(PayrollsVo.class, wrapper);

        return Result.success("查询成功", list);
    }

    @Override
    public ResultPage<PayrollsVo> getPayrollsPage(PayrollsQueryDto dto) {

        if (dto == null) {
            dto = new PayrollsQueryDto();
        }

        String year = dto.getYear();
        if (StringUtils.isBlank(year)) {
            year = DateUtils.getYear();
        }
        String month = DateUtils.mendMonth(dto.getMonth());
        
        String userName = dto.getUserName();

        MPJLambdaWrapper<Payrolls> wrapper = new MPJLambdaWrapper<Payrolls>().selectAll(Payrolls.class)
            .selectAs(Employee::getUserName, PayrollsVo::getUserName)
            .selectAs(Employee::getBasicSalary, PayrollsVo::getBasicSalary)
            // 员工
            .innerJoin(Employee.class, Employee::getId, Payrolls::getUserId)
            .eq(Employee::getDelState, DelState.UNDELETED)
            .eq(Objects.nonNull(dto.getOrganizationId()), Employee::getOrganizationId, dto.getOrganizationId())
            .like(StringUtils.isNotBlank(userName), Employee::getUserName, userName)
            // 工资单
            .eq(Payrolls::getYear, year).eq(Payrolls::getMonth, month);

        Page<PayrollsVo> page = ResultPage.toQueryPage(dto.getPageQuery());

        mapper.selectJoinPage(page, PayrollsVo.class, wrapper);

        return ResultPage.fromPage(page);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<Boolean> payrollComputation(String year, String month) {
        if (StringUtils.isBlank(year)) {
            year = DateUtils.getYear();
        }
        month = DateUtils.mendMonth(month);

        String firstDay = year + "-" + month + "-01";

        List<PayrollComputationDto> list = mapper.selectPayrollComputation(year, month, firstDay);

        // 简单操作
        // 1. 删除工资单
        LambdaQueryWrapper<Payrolls> queryWrapper =
            new LambdaQueryWrapper<Payrolls>().eq(Payrolls::getYear, year).eq(Payrolls::getMonth, month);

        mapper.delete(queryWrapper);

        // 2. 插入新计算的工资单
        String finalYear = year;
        String finalMonth = month;
        List<Payrolls> newList = list.stream().map(p -> {
            Payrolls payrolls = new Payrolls();
            payrolls.setYear(finalYear);
            payrolls.setMonth(finalMonth);
            payrolls.setGrade(p.getGrade());
            payrolls.setBonus(p.getBonus());
            BigDecimal totalWages = p.getBasicSalary().subtract(p.getDeductExpenses()).add(p.getBonus());
            if (totalWages.doubleValue() < 0.0) {
                totalWages = new BigDecimal("0.0");
            }
            payrolls.setDeductExpenses(p.getDeductExpenses());
            payrolls.setTotalWages(totalWages);
            payrolls.setUserId(p.getUserId());
            payrolls.setCreator(UserInfo.getUserId());
            payrolls.setCreateTm(LocalDateTime.now());
            payrolls.setModifier(UserInfo.getUserId());
            payrolls.setModifyTm(LocalDateTime.now());
            return payrolls;
        }).collect(Collectors.toList());

        this.saveBatch(newList, 500);

        return Result.success("一键评级成功", Boolean.TRUE);
    }

    @Override
    public ResponseEntity<InputStreamResource> downloadPayrolls(String year, String month) {
        if (StringUtils.isBlank(year)) {
            year = DateUtils.getYear();
        }

        month = DateUtils.mendMonth(month);

        // 查询工作单数据
        MPJLambdaWrapper<Payrolls> wrapper = new MPJLambdaWrapper<Payrolls>().selectAll(Payrolls.class)
                .selectAs(Employee::getUserName, PayrollsVo::getUserName)
                .selectAs(Employee::getBasicSalary, PayrollsVo::getBasicSalary)
                // 员工
                .innerJoin(Employee.class, Employee::getId, Payrolls::getUserId)
                .eq(Employee::getDelState, DelState.UNDELETED)
                // 工资单
                .eq(Payrolls::getYear, year).eq(Payrolls::getMonth, month);

        List<PayrollsVo> list = mapper.selectJoinList(PayrollsVo.class, wrapper);

        // 临时目录
        String tmpFile = System.getProperty("java.io.tmpdir");
        // 文件名
        String fileName = "工资单_" + year + month + ".xlsx";
        String newFilePath = tmpFile + File.separator + fileName;
        File newFile = new File(newFilePath);
        // 复制模板文件
        copyTemplateFile(newFile);
        if (!newFile.exists()) {
            throw new RuntimeException("拷贝模板文件失败");
        }
        try (InputStream is = new FileInputStream(newFile); XSSFWorkbook workbook = new XSSFWorkbook(is);
             OutputStream os = new FileOutputStream(newFile)) {
            if (ObjectUtils.isNotEmpty(list)) {
                Sheet sheet = workbook.getSheet("工资单");
                AtomicInteger finalBeginRow = new AtomicInteger(1);
                list.forEach(p -> {
                    Row row = sheet.createRow(finalBeginRow.get());
                    // 写数据
                    row.createCell(0).setCellValue(p.getUserName());
                    row.createCell(1).setCellValue(p.getMonth());
                    row.createCell(2).setCellValue(p.getBasicSalary().doubleValue());
                    row.createCell(3).setCellValue(p.getBonus().doubleValue());
                    row.createCell(4).setCellValue(p.getTotalWages().doubleValue());
                    finalBeginRow.getAndIncrement();
                });
            }
            os.flush();
            workbook.write(os);

            return ResponseEntity.ok().header("name", "UTF-8").contentType(MediaType.APPLICATION_OCTET_STREAM)
                .header("Content-disposition",
                    "attachment;filename=" + URLEncoder.encode(fileName,
                        AccessController.doPrivileged(new GetPropertyAction("file.encoding"))))
                .body(new InputStreamResource(new FileInputStream(newFile)));

        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("访问统计下载文件异常");
        }
    }

    /**
     * 拷贝模板文件
     *
     * @param newFile 新文件
     */
    private void copyTemplateFile(File newFile) {
        if (newFile.exists()) {
            newFile.delete();
        }
        try (InputStream is = getClass().getResourceAsStream("/template/payrolls_template.xlsx");
             OutputStream os = new FileOutputStream(newFile)) {
            byte[] bs = new byte[10240];
            if (is != null) {
                int len;
                while ((len = is.read(bs)) != -1) {
                    os.write(bs, 0, len);
                }
                os.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("拷贝模板文件时错误");
        }
    }

}
